﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Ecommerce.Models;
using Library;
using Library.Models;
using Library.MVCHelpers;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Newtonsoft.Json;
using PagedList;

namespace Ecommerce.Controllers
{
    [Authorize]
    public class AdminController : BaseController
    {
        private ApplicationUserManager _userManager;
        private ApplicationRoleManager _roleManager;

        protected override void Initialize(RequestContext requestContext)
        {
            _userManager = requestContext.HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
            _roleManager = requestContext.HttpContext.GetOwinContext().Get<ApplicationRoleManager>();
            base.Initialize(requestContext);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_userManager != null)
                {
                    _userManager.Dispose();
                    _userManager = null;
                }
            }

            base.Dispose(disposing);
        }

        // GET: Admin
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors, Product Viewers")]
        public ActionResult Index()
        {
            return View();
        }

        #region Users
        // GET: Admin/Users
        [Compress]
        [Authorize(Roles = "Administrators")]
        public ActionResult Users()
        {
            return View(_userManager.Users);
        }

        // GET: Admin/EditUser
        [HttpGet]
        [Authorize(Roles = "Administrators")]
        public async Task<ActionResult> EditUser(string id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            var user = await _userManager.FindByIdAsync(id);
            if (user == null)
            {
                return new HttpNotFoundResult();
            }

            return PartialView(new EditUserViewModel(user));
        }

        // POST: Admin/EditUser
        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators")]
        public async Task<ActionResult> EditUser([Bind(Include = "Email,Id,FullName,PhoneNumber,Gender,Birthday,PositionInOrganization")] EditUserViewModel editUser)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    var user = await _userManager.FindByIdAsync(editUser.Id);

                    if (user == null)
                    {
                        return new HttpNotFoundResult();
                    }

                    user.UserName = editUser.Email;
                    user.Email = editUser.Email;
                    user.FullName = editUser.FullName;
                    user.PhoneNumber = editUser.PhoneNumber;
                    user.Gender = editUser.Gender;
                    user.Birthday = editUser.Birthday;
                    user.PositionInOrganization = editUser.PositionInOrganization;

                    var result = await _userManager.UpdateAsync(user);

                    if (!result.Succeeded)
                    {
                        foreach (var error in result.Errors)
                        {
                            ModelState.AddModelError(string.Empty, NOWCulture.GetString(error));
                        }
                        LogHelper.Write(result.Errors);
                        return PartialView(editUser);
                    }

                    return new JsonResult()
                    {
                        Data = new
                        {
                            @status = HttpStatusCode.OK,
                            @id = user.Id,
                            @html = RenderPartialViewToString("_UserRow", user)
                        }
                    };
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError(string.Empty, string.Format(NOWCulture.GetString("Update {0} failed."), NOWCulture.GetString("user")));
                    LogHelper.Write(ex);
                }
            }

            return PartialView(editUser);
        }


        // GET: Admin/CreateUser
        [HttpGet]
        [Authorize(Roles = "Administrators")]
        public ActionResult CreateUser()
        {
            var user = new ApplicationUser();
            return PartialView(new CreateUserViewModel(user));
        }

        // POST: Admin/CreateUser
        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators")]
        public async Task<ActionResult> CreateUser([Bind(Include = "Email,Password,ConfirmPassword,FullName,PhoneNumber,Gender,Birthday,PositionInOrganization")] CreateUserViewModel createUser)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    var user = new ApplicationUser
                    {
                        UserName = createUser.Email,
                        Email = createUser.Email,
                        FullName = createUser.FullName,
                        PhoneNumber = createUser.PhoneNumber,
                        Gender = createUser.Gender,
                        Birthday = createUser.Birthday,
                        PositionInOrganization = createUser.PositionInOrganization
                    };

                    var result = await _userManager.CreateAsync(user, createUser.ConfirmPassword);

                    if (!result.Succeeded)
                    {
                        foreach (var error in result.Errors)
                        {
                            ModelState.AddModelError(string.Empty, NOWCulture.GetString(error));
                        }
                        LogHelper.Write(result.Errors);
                        return PartialView(createUser);
                    }

                    return new JsonResult()
                    {
                        Data = new
                        {
                            @status = HttpStatusCode.OK,
                            @id = user.Id,
                            @html = RenderPartialViewToString("_UserRow", user)
                        }
                    };
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError(string.Empty, string.Format(NOWCulture.GetString("Update {0} failed."), NOWCulture.GetString("user")));
                    LogHelper.Write(ex);
                }
            }

            return PartialView(createUser);
        }

        [HttpPost]
        [Authorize(Roles = "Administrators")]
        public async Task<ActionResult> RemoveUser(string id)
        {
            if (string.IsNullOrEmpty(id))
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("User")));
            }

            var user = await _userManager.FindByIdAsync(id);
            if (user == null)
            {
                return new HttpNotFoundResult(string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("User")));
            }

            IdentityResult result;
            foreach (var role in user.Roles)
            {
                result = await _userManager.RemoveFromRoleAsync(id, role.RoleId);
                if (result != IdentityResult.Success)
                {
                    LogHelper.Write(result.Errors);
                    return new HttpStatusCodeResult(HttpStatusCode.NotAcceptable, string.Format(NOWCulture.GetString("Can not remove {0}{1}."), NOWCulture.GetString("User"), " '" + user.Email + "'"));
                }
            }

            result = await _userManager.DeleteAsync(user);
            if (result != IdentityResult.Success)
            {
                LogHelper.Write(result.Errors);
                return new HttpStatusCodeResult(HttpStatusCode.NotAcceptable, string.Format(NOWCulture.GetString("Can not remove {0}{1}."), NOWCulture.GetString("User"), " '" + user.Email + "'"));
            }

            return new HttpStatusCodeResult(HttpStatusCode.OK);
        }

        [HttpGet]
        [Authorize(Roles = "Administrators")]
        public async Task<ActionResult> ChangeUserPassword(string id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            var user = await _userManager.FindByIdAsync(id);
            if (user == null)
            {
                return new HttpNotFoundResult();
            }

            return PartialView(new ChangeUserPasswordModel(user));
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators")]
        public async Task<ActionResult> ChangeUserPassword(ChangeUserPasswordModel editUser)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    var user = await _userManager.FindByIdAsync(editUser.Id);

                    if (user == null)
                    {
                        return new HttpNotFoundResult();
                    }

                    var result = await _userManager.ChangePasswordAsync(editUser.Id, editUser.Password);

                    if (!result.Succeeded)
                    {
                        foreach (var error in result.Errors)
                        {
                            ModelState.AddModelError(string.Empty, NOWCulture.GetString(error));
                        }
                        LogHelper.Write(result.Errors);
                        return PartialView(editUser);
                    }

                    return new JsonResult()
                    {
                        Data = new
                        {
                            @status = HttpStatusCode.OK
                        }
                    };
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError(string.Empty, string.Format(NOWCulture.GetString("Update {0} failed."), NOWCulture.GetString("user")));
                    LogHelper.Write(ex);
                }
            }
            return PartialView(editUser);
        }

        #endregion

        #region Roles

        // GET: Admin/Roles
        [Compress]
        [Authorize(Roles = "Administrators")]
        public ActionResult Roles()
        {
            return View(_roleManager.Roles);
        }

        // GET: Admin/EditRole
        [HttpGet]
        [Authorize(Roles = "Administrators")]
        public async Task<ActionResult> EditRole(string id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            var role = await _roleManager.FindByIdAsync(id);
            if (role == null)
            {
                return new HttpNotFoundResult();
            }

            return PartialView(new EditRoleViewModel(role));
        }

        // POST: Admin/EditUser
        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators")]
        public async Task<ActionResult> EditRole(EditRoleViewModel editRole)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    var role = await _roleManager.FindByIdAsync(editRole.Id);

                    if (role == null)
                    {
                        return new HttpNotFoundResult();
                    }

                    role.Description = editRole.Description;

                    var result = await _roleManager.UpdateAsync(role);

                    if (!result.Succeeded)
                    {
                        foreach (var error in result.Errors)
                        {
                            ModelState.AddModelError(string.Empty, NOWCulture.GetString(error));
                        }
                        LogHelper.Write(result.Errors);
                        return PartialView(editRole);
                    }

                    return new JsonResult()
                    {
                        Data = new
                        {
                            @status = HttpStatusCode.OK,
                            @id = role.Id,
                            @html = RenderPartialViewToString("_RoleRow", role)
                        }
                    };
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError(string.Empty, string.Format(NOWCulture.GetString("Update {0} failed."), NOWCulture.GetString("role")));
                    LogHelper.Write(ex);
                }
            }

            return PartialView(editRole);
        }
        #endregion

        #region Pages
        [Compress]
        [Authorize(Roles = "Administrators,Editors,Page Editors,Page Viewers")]
        public ActionResult Pages()
        {
            return View(DatabaseConnector.Pages);
        }

        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators,Editors,Page Editors,Page Viewers")]
        public ActionResult SearchPages(string keywords)
        {
            if (!string.IsNullOrEmpty(keywords))
            {
                keywords = keywords.ToLower();
            }

            var sb = new StringBuilder();
            using (var db = DatabaseConnector)
            {
                foreach (var p in db.Pages)
                {
                    if (string.IsNullOrEmpty(keywords))
                    {
                        sb.Append(RenderPartialViewToString("_PageRow", p));
                    }
                    else
                    {
                        var pageCulture = p.PageCultures.Where(pc => pc.Culture == NOWCulture.CurrentCulture
                                && (pc.WebTitle.ToLower().Contains(keywords) ||
                                    pc.WebDescription.ToLower().Contains(keywords) ||
                                    pc.Keywords.ToLower().Contains(keywords)));

                        if (pageCulture.Any())
                        {
                            sb.Append(RenderPartialViewToString("_PageRow", p));
                        }
                    }
                }
            }

            return Content(sb.ToString());
        }

        [HttpPost]
        [Compress]
        [Authorize(Roles = "Administrators,Editors,Page Editors")]
        public async Task<ActionResult> RemovePage(int id)
        {
            try
            {
                using (var db = DatabaseConnector)
                {
                    var page = await db.Pages.SingleOrDefaultAsync(p => p.Id == id);
                    if (page == null)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Page")));
                    }

                    db.PageCultures.RemoveRange(page.PageCultures);
                    db.Pages.Remove(page);
                    var result = await db.SaveChangesAsync();
                    if (result == 0)
                    {
                        var pageCulture = page.PageCultures.SingleOrDefault(pc => pc.Culture == CurrentCulture) ?? page.PageCultures.FirstOrDefault();
                        Debug.Assert(pageCulture != null, "pageCulture != null");
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified, string.Format(NOWCulture.GetString("Can not remove {0}{1}."), NOWCulture.GetString("Page"), " '" + pageCulture.WebTitle + "'"));
                    }

                    return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK);
                }
            }
            catch (Exception ex)
            {
                LogHelper.Write(ex);
                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.InternalServerError, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
            }
        }

        private List<SelectListItem> GetPageGroupSelectList(string selectedCulture, Page page)
        {
            if (page == null)
                return null;

            using (var db = DatabaseConnector)
            {
                var selectedList = new List<SelectListItem>
                {
                    new SelectListItem()
                    {
                        Text = string.Empty,
                        Value = "-1"
                    }
                };
                selectedList.AddRange(from pageGroup in db.PageGroups
                                      let pgCulture = pageGroup.PageGroupCultures.FirstOrDefault(pgc => pgc.Culture == selectedCulture) ?? pageGroup.PageGroupCultures.FirstOrDefault()
                                      where pgCulture != null
                                      select new SelectListItem()
                                      {
                                          Selected = (pageGroup.Id == page.PageGroupId),
                                          Text = pgCulture.Name,
                                          Value = pageGroup.Id.ToString()
                                      });
                return selectedList;
            }
        }

        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators,Editors,Page Editors")]
        public async Task<ActionResult> EditPage(string selectedCulture, int id = -1)
        {
            using (var db = DatabaseConnector)
            {
                ViewBag.IsEdit = true;

                var page = await db.Pages.SingleOrDefaultAsync(p => p.Id == id);
                if (page == null)
                {
                    page = new Page();
                    ViewBag.IsEdit = false;
                }
                var model = new EditPageViewModel(page, selectedCulture);

                if (string.IsNullOrEmpty(selectedCulture))
                {
                    selectedCulture = NOWCulture.CurrentCulture;
                }

                ViewBag.SelectedList = GetPageGroupSelectList(selectedCulture, page);

                return View(model);
            }
        }

        [HttpPost]
        [Compress]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators, Editors, Page Editors")]
        public async Task<ActionResult> EditPage(EditPageViewModel editModel)
        {
            Page page = null;
            if (ModelState.IsValid)
            {
                try
                {
                    using (var db = DatabaseConnector)
                    {
                        if (editModel.Id > 0)
                        {
                            page = await db.Pages.SingleOrDefaultAsync(p => p.Id == editModel.Id);
                            if (page == null)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Page")));
                            }

                            page.PageGroupId = editModel.PageGroupId > 0 ? editModel.PageGroupId : null;

                            var pageCulture = page.PageCultures.SingleOrDefault(pcc => pcc.Culture == editModel.Culture);
                            if (pageCulture == null)
                            {
                                pageCulture = new PageCulture
                                {
                                    Culture = editModel.Culture,
                                    Keywords = editModel.Keywords,
                                    WebTitle = editModel.WebTitle,
                                    WebDescription = editModel.WebDescription,
                                    Url = editModel.Url.ToURL(),
                                    Content = editModel.Content
                                };
                                page.PageCultures.Add(pageCulture);
                            }
                            else
                            {
                                // Edit product category culture
                                pageCulture.Culture = editModel.Culture;
                                pageCulture.Keywords = editModel.Keywords;
                                pageCulture.WebTitle = editModel.WebTitle;
                                pageCulture.WebDescription = editModel.WebDescription;
                                pageCulture.Url = editModel.Url.ToURL();
                                pageCulture.Content = editModel.Content;
                            }
                        }
                        else
                        {
                            page = new Page { PageGroupId = editModel.PageGroupId > 0 ? editModel.PageGroupId : null };

                            // Create product category culture
                            page.PageCultures.Add(new PageCulture
                            {
                                Culture = editModel.Culture,
                                Keywords = editModel.Keywords,
                                WebTitle = editModel.WebTitle,
                                WebDescription = editModel.WebDescription,
                                Url = editModel.Url.ToURL(),
                                Content = editModel.Content
                            });

                            db.Pages.Add(page);
                        }

                        if (db.ChangeTracker.HasChanges())
                        {
                            // Has changes
                            var result = await db.SaveChangesAsync();
                            if (result > 0)
                            {
                                return RedirectToAction("Pages");
                            }
                        }
                        else
                        {
                            // Has no changes
                            return RedirectToAction("Pages");
                        }
                    }
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError(string.Empty, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
                    LogHelper.Write(ex);
                }
            }

            ViewBag.IsEdit = editModel.Id > 0;
            ViewBag.SelectedList = GetPageGroupSelectList(editModel.Culture, page);
            return View(editModel);
        }

        [Compress]
        [Authorize(Roles = "Administrators, Editors, Page Editors")]
        public async Task<ActionResult> PageGallery(string selectedCulture, int id)
        {
            using (var db = DatabaseConnector)
            {
                var page = await db.Pages.SingleOrDefaultAsync(i => i.Id == id);

                if (page == null)
                {
                    return HttpNotFound();
                }

                if (string.IsNullOrWhiteSpace(selectedCulture))
                {
                    selectedCulture = NOWCulture.CurrentCulture;
                }

                var pageCulture = page.PageCultures.SingleOrDefault(i => i.Culture == selectedCulture);

                if (pageCulture == null)
                {
                    return HttpNotFound();
                }

                var imageModels = string.IsNullOrEmpty(pageCulture.Gallery) ? new List<PageImage>() : JsonConvert.DeserializeObject<List<PageImage>>(pageCulture.Gallery);
                var images = imageModels.Select(imgModel => new PageImageViewModel(id, selectedCulture, imgModel)).ToList();
                ViewBag.Images = images;

                return View(pageCulture);
            }
        }

        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Page Editors")]
        public async Task<ActionResult> EditPageImage(int refId, string selectedCulture, string imageSrc)
        {
            using (var db = DatabaseConnector)
            {
                var page = await db.Pages.SingleOrDefaultAsync(i => i.Id == refId);

                if (page == null)
                {
                    return HttpNotFound();
                }

                if (string.IsNullOrWhiteSpace(selectedCulture))
                {
                    selectedCulture = NOWCulture.CurrentCulture;
                }

                var productCulture = page.PageCultures.SingleOrDefault(i => i.Culture == selectedCulture);

                if (productCulture == null)
                {
                    return HttpNotFound();
                }

                PageImageViewModel model = null;
                if (string.IsNullOrEmpty(imageSrc))
                {
                    model = new PageImageViewModel { PageId = refId, Culture = selectedCulture };
                }
                else
                {
                    if (!string.IsNullOrEmpty(productCulture.Gallery))
                    {
                        List<PageImage> images = JsonConvert.DeserializeObject<List<PageImage>>(productCulture.Gallery);
                        var image = images.SingleOrDefault(i => i.Src == imageSrc);
                        if (image != null)
                        {
                            model = new PageImageViewModel(refId, selectedCulture, image);
                        }
                    }
                }

                if (model == null)
                {
                    model = new PageImageViewModel { PageId = refId, Culture = selectedCulture };
                }

                ViewBag.IsEdit = !string.IsNullOrEmpty(model.Src);
                return PartialView(model);
            }
        }

        [HttpPost]
        [Compress]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators, Editors, Page Editors")]
        public async Task<ActionResult> EditPageImage(PageImageViewModel editModel)
        {
            try
            {
                if (string.IsNullOrEmpty(editModel.Src))
                {
                    ModelState.AddModelError("Image", NOWCulture.GetString("Please choose either a GIF, JPG or PNG image."));
                }

                if (ModelState.IsValid)
                {
                    using (var db = DatabaseConnector)
                    {
                        var page = await db.Pages.SingleOrDefaultAsync(i => i.Id == editModel.PageId);
                        if (page == null)
                        {
                            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.BadRequest, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Page")));
                        }

                        var pageCulture = page.PageCultures.SingleOrDefault(i => i.Culture == editModel.Culture);
                        if (pageCulture == null)
                        {
                            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.BadRequest, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("The page informations of selected language")));
                        }

                        if (!string.IsNullOrEmpty(editModel.Src))
                        {
                            List<PageImage> images = string.IsNullOrEmpty(pageCulture.Gallery) ? new List<PageImage>() : JsonConvert.DeserializeObject<List<PageImage>>(pageCulture.Gallery);

                            var image = images.SingleOrDefault(i => i.Src == editModel.Src);
                            if (image != null)
                            {
                                image.Src = editModel.Src;
                                image.Caption = editModel.Caption;
                                image.AltText = editModel.AltText;
                                image.IsExtraSmallAvatar = editModel.IsExtraSmallAvatar;
                                image.IsSmallAvatar = editModel.IsSmallAvatar;
                                image.IsMediumAvatar = editModel.IsMediumAvatar;
                                image.IsLargeAvatar = editModel.IsLargeAvatar;
                            }
                            else
                            {
                                image = new PageImage
                                {
                                    Src = editModel.Src,
                                    Caption = editModel.Caption,
                                    AltText = editModel.AltText,
                                    IsExtraSmallAvatar = editModel.IsExtraSmallAvatar,
                                    IsSmallAvatar = editModel.IsSmallAvatar,
                                    IsMediumAvatar = editModel.IsMediumAvatar,
                                    IsLargeAvatar = editModel.IsLargeAvatar
                                };
                                images.Add(image);
                            }

                            pageCulture.Gallery = JsonConvert.SerializeObject(images);
                            var result = await db.SaveChangesAsync();
                            if (result > 0)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, editModel.Src, RenderPartialViewToString("_PageImageView", editModel));
                            }
                            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ModelState.AddModelError(string.Empty, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
                LogHelper.Write(ex);
            }

            ViewBag.IsEdit = !string.IsNullOrEmpty(editModel.Src);
            return PartialView(editModel);
        }

        [HttpPost]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> RemovePageImage(int refId, string selectedCulture, string imageSrc)
        {
            try
            {
                using (var db = DatabaseConnector)
                {
                    var page = await db.Pages.SingleOrDefaultAsync(i => i.Id == refId);

                    if (page == null)
                    {
                        return HttpNotFound();
                    }

                    if (string.IsNullOrWhiteSpace(selectedCulture))
                    {
                        selectedCulture = NOWCulture.CurrentCulture;
                    }

                    var pageCulture = page.PageCultures.SingleOrDefault(i => i.Culture == selectedCulture);

                    if (pageCulture == null)
                    {
                        return HttpNotFound();
                    }

                    if (!string.IsNullOrEmpty(pageCulture.Gallery))
                    {
                        List<PageImage> images = JsonConvert.DeserializeObject<List<PageImage>>(pageCulture.Gallery);
                        var image = images.SingleOrDefault(i => i.Src == imageSrc);
                        if (image != null)
                        {
                            images.Remove(image);

                            pageCulture.Gallery = JsonConvert.SerializeObject(images);
                            var result = await db.SaveChangesAsync();
                            if (result > 0)
                            {
                                Utilities.RemoveImage(imageSrc);
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogHelper.Write(ex);
                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.InternalServerError, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
            }

            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified);
        }
        #endregion

        #region Page group

        [Compress]
        [Authorize(Roles = "Administrators, Editors, Page Editors, Page Viewers")]
        public ActionResult PageGroups()
        {
            return View(DatabaseConnector.PageGroups);
        }

        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Page Editors, Page Viewers")]
        public ActionResult SearchPageGroups(string keywords)
        {
            if (!string.IsNullOrEmpty(keywords))
            {
                keywords = keywords.ToLower();
            }

            var sb = new StringBuilder();
            using (var db = DatabaseConnector)
            {
                var pageGroups = db.PageGroups;

                foreach (var pg in pageGroups)
                {
                    if (string.IsNullOrEmpty(keywords))
                    {
                        sb.Append(RenderPartialViewToString("_PageGroupRow", pg));
                    }
                    else
                    {
                        var pgCulture = pg.PageGroupCultures.Where(i => i.Culture == NOWCulture.CurrentCulture
                            && (i.Name.ToLower().Contains(keywords) ||
                                (!string.IsNullOrEmpty(i.Description) && i.Description.ToLower().Contains(keywords)) ||
                                i.Keywords.ToLower().Contains(keywords) ||
                                i.WebTitle.ToLower().Contains(keywords) ||
                                i.WebDescription.ToLower().Contains(keywords)));

                        if (pgCulture.Any())
                        {
                            sb.Append(RenderPartialViewToString("_PageGroupRow", pg));
                        }
                    }
                }
            }

            return Content(sb.ToString());
        }

        // GET: Admin/EditProductCategory
        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Page Editors")]
        public async Task<ActionResult> EditPageGroup(int id, string selectedCulture)
        {
            using (var db = DatabaseConnector)
            {
                ViewBag.IsEdit = true;

                var pageGroup = await db.PageGroups.SingleOrDefaultAsync(pc => pc.Id == id);
                if (pageGroup == null)
                {
                    pageGroup = new PageGroup();
                    ViewBag.IsEdit = false;
                }

                return PartialView(new EditPageGroupViewModel(pageGroup, selectedCulture));
            }
        }

        [HttpPost]
        [Compress]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators, Editors, Page Editors")]
        public async Task<ActionResult> EditPageGroup(EditPageGroupViewModel editModel)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    using (var db = DatabaseConnector)
                    {
                        PageGroup pageGroup;
                        if (editModel.Id > 0)
                        {
                            //Edit
                            pageGroup = await db.PageGroups.SingleOrDefaultAsync(pg => pg.Id == editModel.Id);
                            if (pageGroup == null)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Page group")));
                            }

                            var pgCulture = pageGroup.PageGroupCultures.SingleOrDefault(pgc => pgc.Culture == editModel.Culture);
                            if (pgCulture == null)
                            {
                                // Create culture
                                pgCulture = new PageGroupCulture
                                {
                                    Culture = editModel.Culture,
                                    Name = editModel.Name,
                                    Description = editModel.Description,
                                    Keywords = editModel.Keywords,
                                    WebTitle = editModel.WebTitle,
                                    WebDescription = editModel.WebDescription,
                                    Url = editModel.Url.ToURL()
                                };
                                pageGroup.PageGroupCultures.Add(pgCulture);
                            }
                            else
                            {
                                // Edit product category culture
                                pgCulture.Culture = editModel.Culture;
                                pgCulture.Name = editModel.Name;
                                pgCulture.Description = editModel.Description;
                                pgCulture.Keywords = editModel.Keywords;
                                pgCulture.WebTitle = editModel.WebTitle;
                                pgCulture.WebDescription = editModel.WebDescription;
                                pgCulture.Url = editModel.Url.ToURL();
                            }
                        }
                        else
                        {
                            // Create product category
                            pageGroup = new PageGroup();

                            // Create product category culture
                            pageGroup.PageGroupCultures.Add(new PageGroupCulture
                            {
                                Culture = editModel.Culture,
                                Name = editModel.Name,
                                Description = editModel.Description,
                                Keywords = editModel.Keywords,
                                WebTitle = editModel.WebTitle,
                                WebDescription = editModel.WebDescription,
                                Url = editModel.Url.ToURL()
                            });

                            pageGroup = db.PageGroups.Add(pageGroup);
                        }

                        if (db.ChangeTracker.HasChanges())
                        {
                            // Has changes
                            var result = await db.SaveChangesAsync();
                            if (result > 0)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, editModel.Id, RenderPartialViewToString("_PageGroupRow", pageGroup));
                            }
                        }
                        else
                        {
                            // Has no changes
                            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, editModel.Id);
                        }
                    }
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError(string.Empty, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
                    LogHelper.Write(ex);
                }
            }

            ViewBag.IsEdit = editModel.Id > 0;
            return PartialView(editModel);
        }

        [HttpPost]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Page Editors")]
        public async Task<ActionResult> RemovePageGroup(int id)
        {
            try
            {
                using (var db = DatabaseConnector)
                {
                    var model = await db.PageGroups.SingleOrDefaultAsync(pc => pc.Id == id);
                    if (model == null)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Page group")));
                    }

                    var modelCulture = model.PageGroupCultures.SingleOrDefault(pcc => pcc.Culture == NOWCulture.CurrentCulture) ?? model.PageGroupCultures.FirstOrDefault();
                    Debug.Assert(modelCulture != null, "bCulture != null");

                    db.PageGroupCultures.RemoveRange(model.PageGroupCultures);
                    db.PageGroups.Remove(model);
                    var result = await db.SaveChangesAsync();
                    if (result == 0)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified, string.Format(NOWCulture.GetString("Can not remove {0}{1}."), NOWCulture.GetString("Page group"), " '" + modelCulture.Name + "'"));
                    }

                    return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK);
                }
            }
            catch (Exception ex)
            {
                LogHelper.Write(ex);
                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.InternalServerError, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
            }
        }

        #endregion

        #region Product categories

        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors, Product Viewers")]
        public ActionResult ProductCategories()
        {
            var productCategories = DatabaseConnector.ProductCategories.OrderBy(pc => pc.DisplayOrder);
            return View(productCategories);
        }

        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors, Product Viewers")]
        public ActionResult SearchProductCategorys(string keywords)
        {
            if (!string.IsNullOrEmpty(keywords))
            {
                keywords = keywords.ToLower();
            }

            var sb = new StringBuilder();
            using (var db = DatabaseConnector)
            {
                var productCategories = db.ProductCategories.OrderBy(pc => pc.DisplayOrder);

                foreach (var pc in productCategories)
                {
                    if (string.IsNullOrEmpty(keywords))
                    {
                        sb.Append(RenderPartialViewToString("_ProductCategoryRow", pc));
                    }
                    else
                    {
                        var pcCulture = pc.ProductCategoryCultures.Where(pcc => pcc.Culture == NOWCulture.CurrentCulture
                            && (pcc.Name.ToLower().Contains(keywords) ||
                                (!string.IsNullOrEmpty(pcc.Description) && pcc.Description.ToLower().Contains(keywords)) ||
                                pcc.Keywords.ToLower().Contains(keywords) ||
                                pcc.WebTitle.ToLower().Contains(keywords) ||
                                pcc.WebDescription.ToLower().Contains(keywords)));

                        if (pcCulture.Any())
                        {
                            sb.Append(RenderPartialViewToString("_ProductCategoryRow", pc));
                        }
                    }
                }
            }

            return Content(sb.ToString());
        }

        // GET: Admin/EditProductCategory
        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators,Editors,Product Editors")]
        public async Task<ActionResult> EditProductCategory(int id, string selectedCulture)
        {
            using (var db = DatabaseConnector)
            {
                ViewBag.IsEdit = true;

                var productCategory = await db.ProductCategories.SingleOrDefaultAsync(pc => pc.Id == id);
                if (productCategory == null)
                {
                    productCategory = new ProductCategory();
                    ViewBag.IsEdit = false;
                }

                var childs = EditProductCategoryViewModel.GetChildsOfProductCategory(productCategory);
                var exceptionIds = new List<int> { id };
                if (childs != null)
                {
                    exceptionIds.AddRange(childs.Select(p => p.Id));
                }

                var otherProductCategories = db.ProductCategories.Where(pc => !exceptionIds.Contains(pc.Id));

                var selectedList = new List<SelectListItem>
                {
                    new SelectListItem()
                    {
                        Text = string.Empty,
                        Value = "-1"
                    }
                };

                foreach (var pc in otherProductCategories)
                {
                    var cul = pc.ProductCategoryCultures.SingleOrDefault(c => c.Culture == selectedCulture) ??
                              pc.ProductCategoryCultures.FirstOrDefault();
                    if (cul != null)
                    {
                        selectedList.Add(new SelectListItem()
                        {
                            Selected = (pc.Id == productCategory.ParentId),
                            Text = cul.Name,
                            Value = pc.Id.ToString()
                        });
                    }
                }
                ViewBag.SelectedList = selectedList;

                return PartialView(new EditProductCategoryViewModel(productCategory, selectedCulture));
            }
        }

        [HttpPost]
        [Compress]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators,Editors,Product Editors")]
        public async Task<ActionResult> EditProductCategory(EditProductCategoryViewModel editModel)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    using (var db = DatabaseConnector)
                    {
                        ProductCategory productCategory;
                        if (editModel.Id > 0)
                        {
                            //Edit product category
                            productCategory = await db.ProductCategories.SingleOrDefaultAsync(pc => pc.Id == editModel.Id);
                            if (productCategory == null)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Product category")));
                            }

                            productCategory.ParentId = editModel.ParentId > 0 ? editModel.ParentId : null;

                            var productCategoryCulture = productCategory.ProductCategoryCultures.SingleOrDefault(pcc => pcc.Culture == editModel.Culture);
                            if (productCategoryCulture == null)
                            {
                                // Create product category culture
                                productCategoryCulture = new ProductCategoryCulture
                                {
                                    Culture = editModel.Culture,
                                    Name = editModel.Name,
                                    Description = editModel.Description,
                                    Keywords = editModel.Keywords,
                                    WebTitle = editModel.WebTitle,
                                    WebDescription = editModel.WebDescription,
                                    Url = editModel.Url.ToURL()
                                };
                                productCategory.ProductCategoryCultures.Add(productCategoryCulture);
                            }
                            else
                            {
                                // Edit product category culture
                                productCategoryCulture.Culture = editModel.Culture;
                                productCategoryCulture.Name = editModel.Name;
                                productCategoryCulture.Description = editModel.Description;
                                productCategoryCulture.Keywords = editModel.Keywords;
                                productCategoryCulture.WebTitle = editModel.WebTitle;
                                productCategoryCulture.WebDescription = editModel.WebDescription;
                                productCategoryCulture.Url = editModel.Url.ToURL();
                            }
                        }
                        else
                        {
                            // Create product category
                            productCategory = new ProductCategory
                            {
                                ParentId = editModel.ParentId > 0 ? editModel.ParentId : null
                            };

                            // Create product category culture
                            productCategory.ProductCategoryCultures.Add(new ProductCategoryCulture()
                            {
                                Culture = editModel.Culture,
                                Name = editModel.Name,
                                Description = editModel.Description,
                                Keywords = editModel.Keywords,
                                WebTitle = editModel.WebTitle,
                                WebDescription = editModel.WebDescription,
                                Url = editModel.Url.ToURL()
                            });

                            productCategory = db.ProductCategories.Add(productCategory);
                        }

                        if (db.ChangeTracker.HasChanges())
                        {
                            // Has changes
                            var result = await db.SaveChangesAsync();
                            if (result > 0)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, editModel.Id, RenderPartialViewToString("_ProductCategoryRow", productCategory));
                            }
                        }
                        else
                        {
                            // Has no changes
                            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, editModel.Id);
                        }
                    }
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError(string.Empty, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
                    LogHelper.Write(ex);
                }
            }

            ViewBag.IsEdit = editModel.Id > 0;
            return PartialView(editModel);
        }

        [HttpPost]
        [Compress]
        [Authorize(Roles = "Administrators,Editors,Product Editors")]
        public async Task<ActionResult> RemoveProductCategory(int id)
        {
            try
            {
                using (var db = DatabaseConnector)
                {
                    var productCategory = await db.ProductCategories.SingleOrDefaultAsync(pc => pc.Id == id);
                    if (productCategory == null)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Product category")));
                    }

                    var pcCulture = productCategory.ProductCategoryCultures.SingleOrDefault(pcc => pcc.Culture == NOWCulture.CurrentCulture) ?? productCategory.ProductCategoryCultures.FirstOrDefault();
                    Debug.Assert(pcCulture != null, "pcCulture != null");

                    var childs = EditProductCategoryViewModel.GetChildsOfProductCategory(productCategory);
                    if (childs != null && childs.Count > 0)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotAcceptable,
                            string.Format(NOWCulture.GetString("Can not remove {0}{1}."), NOWCulture.GetString("Product category"), " '" + pcCulture.Name + "'")
                            + " "
                            + NOWCulture.GetString("Please remove product category childs."));
                    }

                    db.ProductCategories.Remove(productCategory);
                    var result = await db.SaveChangesAsync();
                    if (result == 0)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified, string.Format(NOWCulture.GetString("Can not remove {0}{1}."), NOWCulture.GetString("Product category"), " '" + pcCulture.Name + "'"));
                    }

                    return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK);
                }
            }
            catch (Exception ex)
            {
                LogHelper.Write(ex);
                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.InternalServerError, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
            }
        }

        private List<SelectListItem> GetProductCategorySelectList(EcommerceEntities db, string selectedCulture, EditProductViewModel product, bool showEmptyOption = true)
        {
            var selectedList = new List<SelectListItem>();
            if (showEmptyOption)
            {
                selectedList.Add(new SelectListItem
                {
                    Text = string.Empty,
                    Value = "0"
                });
            }

            if (string.IsNullOrWhiteSpace(selectedCulture))
            {
                selectedCulture = NOWCulture.CurrentCulture;
            }

            if (product == null || product.ProductCategoryId <= 0)
            {
                selectedList.AddRange(from productCategory in db.ProductCategories
                                      let pcCulture = productCategory.ProductCategoryCultures.FirstOrDefault(i => i.Culture == selectedCulture) ?? productCategory.ProductCategoryCultures.FirstOrDefault()
                                      where pcCulture != null
                                      select new SelectListItem()
                                      {
                                          Text = pcCulture.Name,
                                          Value = productCategory.Id.ToString()
                                      });
            }
            else
            {
                selectedList.AddRange(from productCategory in db.ProductCategories
                                      let pcCulture = productCategory.ProductCategoryCultures.FirstOrDefault(i => i.Culture == selectedCulture) ?? productCategory.ProductCategoryCultures.FirstOrDefault()
                                      where pcCulture != null
                                      select new SelectListItem()
                                      {
                                          Selected = productCategory.Id == product.ProductCategoryId,
                                          Text = pcCulture.Name,
                                          Value = productCategory.Id.ToString()
                                      });
            }

            return selectedList;
        }

        #endregion

        #region Specifications

        [Authorize(Roles = "Administrators, Editors, Product Editors, Product Viewers")]
        public async Task<ActionResult> Specifications(int productCategoryId = -1)
        {
            var productCategory = await DatabaseConnector.ProductCategories.SingleOrDefaultAsync(pc => pc.Id == productCategoryId);
            if (productCategory == null)
            {
                return HttpNotFound();
            }

            var productCategoryCulture = productCategory.ProductCategoryCultures.SingleOrDefault(i => i.Culture == NOWCulture.CurrentCulture) ?? productCategory.ProductCategoryCultures.First();
            ViewBag.ProductCategoryName = productCategoryCulture.Name;

            ViewBag.RefId = productCategoryId;
            return View(productCategory.SpecificationGroups.OrderBy(i => i.DisplayOrder));
        }

        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> EditSpecificationGroup(int id, string selectedCulture, int refId)
        {
            using (var db = DatabaseConnector)
            {
                ViewBag.IsEdit = true;

                var model = await db.SpecificationGroups.SingleOrDefaultAsync(i => i.Id == id);
                if (model == null)
                {
                    model = new SpecificationGroup
                    {
                        ProductCategoryId = refId
                    };
                    ViewBag.IsEdit = false;
                }

                return PartialView(new EditSpecificationGroupViewModel(model, selectedCulture));
            }
        }

        [HttpPost]
        [Compress]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> EditSpecificationGroup(EditSpecificationGroupViewModel editModel)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    using (var db = DatabaseConnector)
                    {
                        SpecificationGroup model;
                        if (editModel.Id > 0)
                        {
                            //Edit
                            model = await db.SpecificationGroups.SingleOrDefaultAsync(i => i.Id == editModel.Id);
                            if (model == null)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Specification group")));
                            }

                            model.ProductCategoryId = editModel.ProductCategoryId;
                            model.DisplayOrder = editModel.DisplayOrder;

                            var modelCulture = model.SpecificationGroupCultures.SingleOrDefault(i => i.Culture == editModel.Culture);
                            if (modelCulture == null)
                            {
                                // Create culture
                                modelCulture = new SpecificationGroupCulture
                                {
                                    Culture = editModel.Culture,
                                    Name = editModel.Name,
                                    Description = editModel.Description
                                };
                                model.SpecificationGroupCultures.Add(modelCulture);
                            }
                            else
                            {
                                // Edit culture
                                modelCulture.Culture = editModel.Culture;
                                modelCulture.Name = editModel.Name;
                                modelCulture.Description = editModel.Description;
                            }
                        }
                        else
                        {
                            // Create
                            model = new SpecificationGroup
                            {
                                ProductCategoryId = editModel.ProductCategoryId,
                                DisplayOrder = editModel.DisplayOrder
                            };

                            // Create culture
                            model.SpecificationGroupCultures.Add(new SpecificationGroupCulture
                            {
                                Culture = editModel.Culture,
                                Name = editModel.Name,
                                Description = editModel.Description
                            });

                            model = db.SpecificationGroups.Add(model);
                        }

                        if (db.ChangeTracker.HasChanges())
                        {
                            // Has changes
                            var result = await db.SaveChangesAsync();
                            if (result > 0)
                            {
                                ViewData["HideSpecifications"] = true;
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, editModel.Id, RenderPartialViewToString("_SpecificationGroupRow", model));
                            }
                        }
                        else
                        {
                            // Has no changes
                            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, editModel.Id);
                        }
                    }
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError(string.Empty, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
                    LogHelper.Write(ex);
                }
            }

            ViewBag.IsEdit = editModel.Id > 0;
            return PartialView(editModel);
        }

        [HttpPost]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> RemoveSpecificationGroup(int id)
        {
            try
            {
                using (var db = DatabaseConnector)
                {
                    var model = await db.SpecificationGroups.SingleOrDefaultAsync(i => i.Id == id);
                    if (model == null)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Specification group")));
                    }

                    var modelCulture = model.SpecificationGroupCultures.SingleOrDefault(pcc => pcc.Culture == NOWCulture.CurrentCulture) ?? model.SpecificationGroupCultures.FirstOrDefault();
                    Debug.Assert(modelCulture != null, "modelCulture != null");

                    if (model.Specifications.Count > 0)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified, string.Format(NOWCulture.GetString("Can not remove {0}{1}.") + " " + NOWCulture.GetString("Please remove its specifications."), NOWCulture.GetString("Specification group"), " '" + modelCulture.Name + "'"));
                    }

                    db.SpecificationGroups.Remove(model);
                    var result = await db.SaveChangesAsync();
                    if (result == 0)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified, string.Format(NOWCulture.GetString("Can not remove {0}{1}."), NOWCulture.GetString("Specification group"), " '" + modelCulture.Name + "'"));
                    }

                    return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK);
                }
            }
            catch (Exception ex)
            {
                LogHelper.Write(ex);
                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.InternalServerError, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
            }
        }

        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> EditSpecification(int id, string selectedCulture, int refId)
        {
            using (var db = DatabaseConnector)
            {
                ViewBag.IsEdit = true;

                var model = await db.Specifications.SingleOrDefaultAsync(i => i.Id == id);
                if (model == null)
                {
                    model = new Specification
                    {
                        SpecificationGroupId = refId
                    };
                    ViewBag.IsEdit = false;
                }

                return PartialView(new EditSpecificationViewModel(model, selectedCulture));
            }
        }

        [HttpPost]
        [Compress]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> EditSpecification(EditSpecificationViewModel editModel)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    using (var db = DatabaseConnector)
                    {
                        Specification model;
                        if (editModel.Id > 0)
                        {
                            //Edit
                            model = await db.Specifications.SingleOrDefaultAsync(i => i.Id == editModel.Id);
                            if (model == null)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Specification")));
                            }

                            model.SpecificationGroupId = editModel.SpecificationGroupId;
                            model.DisplayOrder = editModel.DisplayOrder;

                            var modelCulture = model.SpecificationCultures.SingleOrDefault(i => i.Culture == editModel.Culture);
                            if (modelCulture == null)
                            {
                                // Create culture
                                modelCulture = new SpecificationCulture
                                {
                                    Culture = editModel.Culture,
                                    Name = editModel.Name,
                                    Description = editModel.Description
                                };
                                model.SpecificationCultures.Add(modelCulture);
                            }
                            else
                            {
                                // Edit culture
                                modelCulture.Culture = editModel.Culture;
                                modelCulture.Name = editModel.Name;
                                modelCulture.Description = editModel.Description;
                            }
                        }
                        else
                        {
                            // Create
                            model = new Specification
                            {
                                SpecificationGroupId = editModel.SpecificationGroupId,
                                DisplayOrder = editModel.DisplayOrder
                            };

                            // Create culture
                            model.SpecificationCultures.Add(new SpecificationCulture
                            {
                                Culture = editModel.Culture,
                                Name = editModel.Name,
                                Description = editModel.Description
                            });

                            model = db.Specifications.Add(model);
                        }

                        if (db.ChangeTracker.HasChanges())
                        {
                            // Has changes
                            var result = await db.SaveChangesAsync();
                            if (result > 0)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, editModel.Id, editModel.SpecificationGroupId, RenderPartialViewToString("_SpecificationRow", model));
                            }
                        }
                        else
                        {
                            // Has no changes
                            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, editModel.Id);
                        }
                    }
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError(string.Empty, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
                    LogHelper.Write(ex);
                }
            }

            ViewBag.IsEdit = editModel.Id > 0;
            return PartialView(editModel);
        }

        [HttpPost]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> RemoveSpecification(int id)
        {
            try
            {
                using (var db = DatabaseConnector)
                {
                    var model = await db.Specifications.SingleOrDefaultAsync(i => i.Id == id);
                    if (model == null)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Specification")));
                    }

                    var modelCulture = model.SpecificationCultures.SingleOrDefault(pcc => pcc.Culture == NOWCulture.CurrentCulture) ?? model.SpecificationCultures.FirstOrDefault();
                    Debug.Assert(modelCulture != null, "modelCulture != null");

                    db.Specifications.Remove(model);
                    var result = await db.SaveChangesAsync();
                    if (result == 0)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified, string.Format(NOWCulture.GetString("Can not remove {0}{1}."), NOWCulture.GetString("Specification"), " '" + modelCulture.Name + "'"));
                    }

                    return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK);
                }
            }
            catch (Exception ex)
            {
                LogHelper.Write(ex);
                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.InternalServerError, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
            }
        }

        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public ActionResult SpecificationValues(int id)
        {
            ViewBag.SpecificationId = id;
            return PartialView(DatabaseConnector.SpecificationValues.Where(i => i.SpecificationId == id).OrderByDescending(i => i.Weight));
        }

        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public ActionResult EditSpecificationValue(int id, int specificationId)
        {
            using (var db = DatabaseConnector)
            {
                SpecificationValue model = null;
                if (id > 0)
                {
                    ViewBag.IsEdit = true;
                    model = db.SpecificationValues.SingleOrDefault(i => i.Id == id);
                }

                if (model == null)
                {
                    ViewBag.IsEdit = false;
                    model = new SpecificationValue
                    {
                        SpecificationId = specificationId
                    };
                }
                return PartialView("_SpecificationValueRow", new EditSpecificationValueViewModel(model));

            }
        }

        [HttpPost]
        [Compress]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> EditSpecificationValue(EditSpecificationValueViewModel editModel)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    using (var db = DatabaseConnector)
                    {
                        SpecificationValue model;
                        if (editModel.Id > 0)
                        {
                            //Edit
                            model = await db.SpecificationValues.SingleOrDefaultAsync(i => i.Id == editModel.Id);
                            if (model == null)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Specification value")));
                            }

                            model.Value = editModel.Value;
                            model.Weight = editModel.Weight;
                        }
                        else
                        {
                            // Create
                            model = new SpecificationValue
                            {
                                Value = editModel.Value,
                                Weight = editModel.Weight,
                                SpecificationId = editModel.SpecificationId
                            };

                            model = db.SpecificationValues.Add(model);
                        }

                        if (db.ChangeTracker.HasChanges())
                        {
                            // Has changes
                            var result = await db.SaveChangesAsync();
                            if (result == 0)
                            {
                                ModelState.AddModelError(string.Empty, string.Format(NOWCulture.GetString("Create/Update {0} failed.") + " " + NOWCulture.GetString("Please try again."), NOWCulture.GetString("specification value")));
                                return PartialView("_SpecificationValueRow", editModel);
                            }
                            return PartialView("_SpecificationValueRow", new EditSpecificationValueViewModel(model));
                        }
                    }
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError(string.Empty, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
                    LogHelper.Write(ex);
                }
            }

            ViewBag.IsEdit = editModel.Id > 0;
            return PartialView("_SpecificationValueRow", editModel);
        }

        [HttpPost]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> RemoveSpecificationValue(int id)
        {
            try
            {
                using (var db = DatabaseConnector)
                {
                    var model = await db.SpecificationValues.SingleOrDefaultAsync(i => i.Id == id);
                    if (model != null)
                    {
                        db.SpecificationValues.Remove(model);
                        var result = await db.SaveChangesAsync();
                        if (result > 0)
                        {
                            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK);
                        }
                    }
                }
                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified, string.Format(NOWCulture.GetString("Can not remove {0}{1}."), NOWCulture.GetString("specification value"), string.Empty));
            }
            catch (Exception ex)
            {
                LogHelper.Write(ex);
                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.InternalServerError, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
            }
        }
        #endregion

        #region Brands

        [Compress]
        [Authorize(Roles = "Administrators,Editors,Brand Editors,Brand Viewers")]
        public ActionResult Brands()
        {
            var brands = DatabaseConnector.Brands.OrderBy(pc => pc.DisplayOrder);
            return View(brands);
        }

        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators,Editors,Brand Editors,Brand Viewers")]
        public ActionResult SearchBrands(string keywords)
        {
            if (!string.IsNullOrEmpty(keywords))
            {
                keywords = keywords.ToLower();
            }

            var sb = new StringBuilder();
            using (var db = DatabaseConnector)
            {
                var brands = db.Brands.OrderBy(pc => pc.DisplayOrder);

                foreach (var b in brands)
                {
                    if (string.IsNullOrEmpty(keywords))
                    {
                        sb.Append(RenderPartialViewToString("_BrandRow", b));
                    }
                    else
                    {
                        var bCulture = b.BrandCultures.Where(bc => bc.Culture == NOWCulture.CurrentCulture
                            && (bc.Name.ToLower().Contains(keywords) ||
                                (!string.IsNullOrEmpty(bc.Description) && bc.Description.ToLower().Contains(keywords)) ||
                                bc.Keywords.ToLower().Contains(keywords) ||
                                bc.WebTitle.ToLower().Contains(keywords) ||
                                bc.WebDescription.ToLower().Contains(keywords)));

                        if (bCulture.Any())
                        {
                            sb.Append(RenderPartialViewToString("_BrandRow", b));
                        }
                    }
                }
            }

            return Content(sb.ToString());
        }

        // GET: Admin/EditProductCategory
        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators,Editors,Brand Editors")]
        public async Task<ActionResult> EditBrand(int id, string selectedCulture)
        {
            using (var db = DatabaseConnector)
            {
                ViewBag.IsEdit = true;

                var brand = await db.Brands.SingleOrDefaultAsync(pc => pc.Id == id);
                if (brand == null)
                {
                    brand = new Brand();
                    ViewBag.IsEdit = false;
                }

                return PartialView(new EditBrandViewModel(brand, selectedCulture));
            }
        }

        [HttpPost]
        [Compress]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators, Editors, Brand Editors")]
        public async Task<ActionResult> EditBrand(EditBrandViewModel editModel)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    using (var db = DatabaseConnector)
                    {
                        Brand brand;
                        if (editModel.Id > 0)
                        {
                            //Edit product category
                            brand = await db.Brands.SingleOrDefaultAsync(pc => pc.Id == editModel.Id);
                            if (brand == null)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Brand")));
                            }

                            var brandCulture = brand.BrandCultures.SingleOrDefault(pcc => pcc.Culture == editModel.Culture);
                            if (brandCulture == null)
                            {
                                // Create product category culture
                                brandCulture = new BrandCulture
                                {
                                    Culture = editModel.Culture,
                                    Name = editModel.Name,
                                    Description = editModel.Description,
                                    Keywords = editModel.Keywords,
                                    WebTitle = editModel.WebTitle,
                                    WebDescription = editModel.WebDescription,
                                    Url = editModel.Url.ToURL()
                                };
                                brand.BrandCultures.Add(brandCulture);
                            }
                            else
                            {
                                // Edit product category culture
                                brandCulture.Culture = editModel.Culture;
                                brandCulture.Name = editModel.Name;
                                brandCulture.Description = editModel.Description;
                                brandCulture.Keywords = editModel.Keywords;
                                brandCulture.WebTitle = editModel.WebTitle;
                                brandCulture.WebDescription = editModel.WebDescription;
                                brandCulture.Url = editModel.Url.ToURL();
                            }
                        }
                        else
                        {
                            // Create product category
                            brand = new Brand();

                            // Create product category culture
                            brand.BrandCultures.Add(new BrandCulture
                            {
                                Culture = editModel.Culture,
                                Name = editModel.Name,
                                Description = editModel.Description,
                                Keywords = editModel.Keywords,
                                WebTitle = editModel.WebTitle,
                                WebDescription = editModel.WebDescription,
                                Url = editModel.Url.ToURL()
                            });

                            brand = db.Brands.Add(brand);
                        }

                        if (db.ChangeTracker.HasChanges())
                        {
                            // Has changes
                            var result = await db.SaveChangesAsync();
                            if (result > 0)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, editModel.Id, RenderPartialViewToString("_BrandRow", brand));
                            }
                        }
                        else
                        {
                            // Has no changes
                            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, editModel.Id);
                        }
                    }
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError(string.Empty, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
                    LogHelper.Write(ex);
                }
            }

            ViewBag.IsEdit = editModel.Id > 0;
            return PartialView(editModel);
        }

        [HttpPost]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Brand Editors")]
        public async Task<ActionResult> RemoveBrand(int id)
        {
            try
            {
                using (var db = DatabaseConnector)
                {
                    var brand = await db.Brands.SingleOrDefaultAsync(pc => pc.Id == id);
                    if (brand == null)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Brand")));
                    }

                    var bCulture = brand.BrandCultures.SingleOrDefault(pcc => pcc.Culture == NOWCulture.CurrentCulture) ?? brand.BrandCultures.FirstOrDefault();
                    Debug.Assert(bCulture != null, "bCulture != null");

                    db.Brands.Remove(brand);
                    var result = await db.SaveChangesAsync();
                    if (result == 0)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified, string.Format(NOWCulture.GetString("Can not remove {0}{1}."), NOWCulture.GetString("Brand"), " '" + bCulture.Name + "'"));
                    }

                    return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK);
                }
            }
            catch (Exception ex)
            {
                LogHelper.Write(ex);
                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.InternalServerError, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
            }
        }

        private List<SelectListItem> GetBrandSelectList(EcommerceEntities db, string selectedCulture, EditProductViewModel product, bool showEmptyOption = true)
        {
            var selectedList = new List<SelectListItem>();
            if (showEmptyOption)
            {
                selectedList.Add(new SelectListItem
                {
                    Text = string.Empty,
                    Value = "0"
                });
            }

            if (string.IsNullOrWhiteSpace(selectedCulture))
            {
                selectedCulture = NOWCulture.CurrentCulture;
            }

            if (product == null || product.ProductCategoryId <= 0)
            {
                selectedList.AddRange(from brand in db.Brands
                                      let pcCulture = brand.BrandCultures.FirstOrDefault(i => i.Culture == selectedCulture) ?? brand.BrandCultures.FirstOrDefault()
                                      where pcCulture != null
                                      select new SelectListItem()
                                      {
                                          Text = pcCulture.Name,
                                          Value = brand.Id.ToString()
                                      });
            }
            else
            {
                selectedList.AddRange(from brand in db.Brands
                                      let pcCulture = brand.BrandCultures.FirstOrDefault(i => i.Culture == selectedCulture) ?? brand.BrandCultures.FirstOrDefault()
                                      where pcCulture != null
                                      select new SelectListItem()
                                      {
                                          Selected = brand.Id == product.BrandId,
                                          Text = pcCulture.Name,
                                          Value = brand.Id.ToString()
                                      });
            }

            return selectedList;
        }

        #endregion

        #region Products

        private List<SelectListItem> GetProductStatusList(string selectedCulture, EditProductViewModel product, bool showEmptyOption = true)
        {
            var selectList = new List<SelectListItem>();

            if (showEmptyOption)
            {
                selectList.Add(new SelectListItem
                {
                    Text = string.Empty,
                    Value = "0"
                });
            }

            if (string.IsNullOrWhiteSpace(selectedCulture))
            {
                selectedCulture = NOWCulture.CurrentCulture;
            }

            selectList.Add(new SelectListItem
            {
                Text = NOWCulture.GetString(ProductStatuses.InStock.ToDisplayString(), selectedCulture),
                Value = Convert.ToString((int)ProductStatuses.InStock)
            });
            selectList.Add(new SelectListItem
            {
                Text = NOWCulture.GetString(ProductStatuses.LimitedAvailability.ToDisplayString(), selectedCulture),
                Value = Convert.ToString((int)ProductStatuses.LimitedAvailability)
            });
            selectList.Add(new SelectListItem
            {
                Text = NOWCulture.GetString(ProductStatuses.OnlineOnly.ToDisplayString(), selectedCulture),
                Value = Convert.ToString((int)ProductStatuses.OnlineOnly)
            });
            selectList.Add(new SelectListItem
            {
                Text = NOWCulture.GetString(ProductStatuses.PreOrder.ToDisplayString(), selectedCulture),
                Value = Convert.ToString((int)ProductStatuses.PreOrder)
            });
            selectList.Add(new SelectListItem
            {
                Text = NOWCulture.GetString(ProductStatuses.SoldOut.ToDisplayString(), selectedCulture),
                Value = Convert.ToString((int)ProductStatuses.SoldOut)
            });
            selectList.Add(new SelectListItem
            {
                Text = NOWCulture.GetString(ProductStatuses.InStoreOnly.ToDisplayString(), selectedCulture),
                Value = Convert.ToString((int)ProductStatuses.InStoreOnly)
            });
            selectList.Add(new SelectListItem
            {
                Text = NOWCulture.GetString(ProductStatuses.OutOfStock.ToDisplayString(), selectedCulture),
                Value = Convert.ToString((int)ProductStatuses.OutOfStock)
            });
            selectList.Add(new SelectListItem
            {
                Text = NOWCulture.GetString(ProductStatuses.Discontinued.ToDisplayString(), selectedCulture),
                Value = Convert.ToString((int)ProductStatuses.Discontinued)
            });

            if (product != null)
            {
                foreach (var option in selectList)
                {
                    if (Convert.ToByte(option.Value) == (byte)product.Status)
                    {
                        option.Selected = true;
                        break;
                    }
                }
            }

            return selectList;
        }

        private const int ProductPageSize = 20;

        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors, Product Viewers")]
        public ActionResult Products(string keywords, int? page)
        {
            int pageNumber = (page ?? 1);
            var products = DatabaseConnector.FindAllProducts(NOWCulture.CurrentCulture, keywords);
            return View(products.ToList().ToPagedList(pageNumber, ProductPageSize));
        }

        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> EditProduct(string selectedCulture, int id = -1)
        {
            using (var db = DatabaseConnector)
            {
                ViewBag.IsEdit = true;

                var model = await db.Products.SingleOrDefaultAsync(p => p.Id == id);
                if (model == null)
                {
                    model = new Product();
                    ViewBag.IsEdit = false;
                }

                if (string.IsNullOrEmpty(selectedCulture))
                {
                    selectedCulture = NOWCulture.CurrentCulture;
                }

                var viewModel = new EditProductViewModel(model, selectedCulture);

                // Dropdown list
                ViewBag.ProductCategoryList = GetProductCategorySelectList(db, selectedCulture, viewModel);
                ViewBag.BrandList = GetBrandSelectList(db, selectedCulture, viewModel);
                ViewBag.StatusList = GetProductStatusList(selectedCulture, viewModel);

                return View(viewModel);
            }
        }

        [HttpPost]
        [Compress]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> EditProduct(EditProductViewModel editModel)
        {
            try
            {
                using (var db = DatabaseConnector)
                {
                    // Dropdown list
                    ViewBag.ProductCategoryList = GetProductCategorySelectList(db, editModel.Culture, editModel);
                    ViewBag.BrandList = GetBrandSelectList(db, editModel.Culture, editModel);
                    ViewBag.StatusList = GetProductStatusList(editModel.Culture, editModel);

                    if (editModel.ProductCategoryId <= 0)
                    {
                        ModelState.AddModelError("ProductCategoryId", NOWCulture.GetString("Please select a product category."));
                    }

                    if (editModel.BrandId <= 0)
                    {
                        ModelState.AddModelError("BrandId", NOWCulture.GetString("Please select a brand."));
                    }

                    if (editModel.Banner != null && !Settings.ValidImageTypes.Contains(editModel.Banner.ContentType))
                    {
                        ModelState.AddModelError("AdImage", NOWCulture.GetString("Please choose either a GIF, JPG or PNG image."));
                    }

                    if (ModelState.IsValid)
                    {
                        Product model;
                        var newUrl = editModel.Url.ToURL();
                        if (editModel.Id > 0)
                        {
                            model = await db.Products.SingleOrDefaultAsync(pc => pc.Id == editModel.Id);
                            if (model == null)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Product")));
                            }

                            model.ProductCategoryId = editModel.ProductCategoryId;
                            model.BrandId = editModel.BrandId;
                            model.PriceDisplay = editModel.PriceDisplay;
                            model.StrikethroughPriceDisplay = editModel.StrikethroughPriceDisplay;
                            model.Status = (byte)editModel.Status;

                            var modelCulture = model.ProductCultures.SingleOrDefault(i => i.Culture == editModel.Culture);
                            if (modelCulture != null)
                            {
                                if (!modelCulture.Url.Equals(newUrl))
                                {
                                    if (SEOHelper.UrlIsExists(newUrl))
                                    {
                                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotAcceptable, NOWCulture.GetString("The URL is exists."));
                                    }
                                }

                                modelCulture.Name = editModel.Name;
                                modelCulture.Description = editModel.Description;
                                modelCulture.Keywords = editModel.Keywords;
                                modelCulture.WebTitle = editModel.WebTitle;
                                modelCulture.WebDescription = editModel.WebDescription;
                                modelCulture.SmallArticle = editModel.SmallArticle;
                                modelCulture.Article = editModel.Article;
                                modelCulture.Policies = editModel.Policies;
                                modelCulture.PromotionCaption = editModel.PromotionCaption;
                                modelCulture.PromotionDetails = editModel.PromotionDetails;
                                modelCulture.BannerUrl = editModel.BannerUrl;
                                modelCulture.BannerCaption = editModel.BannerCaption;
                                modelCulture.BannerAlt = editModel.BannerAlt;

                                if (editModel.Banner != null)
                                {
                                    modelCulture.BannerSrc = Utilities.UploadImage(Settings.ProductImageDiretories + editModel.Id, editModel.Banner);
                                }

                                SEOHelper.UpdateUrl(modelCulture.Url, editModel.Url.ToURL(), new SEOHelper.URLInfo(URLTypes.Product, editModel.Id));
                                modelCulture.Url = editModel.Url.ToURL();
                            }
                            else
                            {
                                if (SEOHelper.UrlIsExists(newUrl))
                                {
                                    return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotAcceptable, NOWCulture.GetString("The URL is exists."));
                                }

                                var modelCulure = new ProductCulture
                                {
                                    Culture = editModel.Culture,
                                    Name = editModel.Name,
                                    Description = editModel.Description,
                                    Keywords = editModel.Keywords,
                                    WebTitle = editModel.WebTitle,
                                    WebDescription = editModel.WebDescription,
                                    Url = editModel.Url.ToURL(),
                                    SmallArticle = editModel.SmallArticle,
                                    Article = editModel.Article,
                                    Policies = editModel.Policies,
                                    PromotionCaption = editModel.PromotionCaption,
                                    PromotionDetails = editModel.PromotionDetails,
                                    BannerUrl = editModel.BannerUrl,
                                    BannerCaption = editModel.BannerCaption,
                                    BannerAlt = editModel.BannerAlt
                                };

                                if (editModel.Banner != null)
                                {
                                    modelCulure.BannerSrc = Utilities.UploadImage(Settings.ProductImageDiretories + model.Id, editModel.Banner);
                                }

                                SEOHelper.RegisterUrl(editModel.Url.ToURL(), new SEOHelper.URLInfo(URLTypes.Product, editModel.Id));

                                model.ProductCultures.Add(modelCulure);
                            }

                            if (db.ChangeTracker.HasChanges())
                            {
                                // Has changes                                
                                if (await db.SaveChangesAsync() > 0)
                                {
                                    return RedirectToAction("Products");
                                }
                            }
                        }
                        else
                        {
                            if (SEOHelper.UrlIsExists(newUrl))
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotAcceptable, NOWCulture.GetString("The URL is exists."));
                            }

                            model = new Product
                            {
                                ProductCategoryId = editModel.ProductCategoryId,
                                BrandId = editModel.BrandId,
                                PriceDisplay = editModel.PriceDisplay,
                                StrikethroughPriceDisplay = editModel.StrikethroughPriceDisplay,
                                Status = (byte)editModel.Status
                            };
                            model = db.Products.Add(model);

                            // Has changes
                            if (await db.SaveChangesAsync() > 0)
                            {
                                var modelCulure = new ProductCulture
                                {
                                    Culture = editModel.Culture,
                                    Name = editModel.Name,
                                    Description = editModel.Description,
                                    Keywords = editModel.Keywords,
                                    WebTitle = editModel.WebTitle,
                                    WebDescription = editModel.WebDescription,
                                    Url = editModel.Url.ToURL(),
                                    SmallArticle = editModel.SmallArticle,
                                    Article = editModel.Article,
                                    Policies = editModel.Policies,
                                    PromotionCaption = editModel.PromotionCaption,
                                    PromotionDetails = editModel.PromotionDetails,
                                    BannerUrl = editModel.BannerUrl,
                                    BannerCaption = editModel.BannerCaption,
                                    BannerAlt = editModel.BannerAlt
                                };

                                if (editModel.Banner != null)
                                {
                                    modelCulure.BannerSrc = Utilities.UploadImage(Settings.ProductImageDiretories + model.Id, editModel.Banner);
                                }

                                SEOHelper.RegisterUrl(editModel.Url.ToURL(), new SEOHelper.URLInfo(URLTypes.Product, editModel.Id));

                                model.ProductCultures.Add(modelCulure);
                                if (await db.SaveChangesAsync() > 0)
                                {
                                    return RedirectToAction("Products");
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ModelState.AddModelError(string.Empty, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
                LogHelper.Write(ex);
            }

            ViewBag.IsEdit = editModel.Id > 0;
            return PartialView(editModel);
        }

        [HttpPost]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> RemoveProduct(int id)
        {
            try
            {
                using (var db = DatabaseConnector)
                {
                    var model = await db.Products.SingleOrDefaultAsync(pc => pc.Id == id);
                    if (model == null)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotFound, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Product")));
                    }

                    var modelCulture = model.ProductCultures.SingleOrDefault(pcc => pcc.Culture == NOWCulture.CurrentCulture) ?? model.ProductCultures.FirstOrDefault();
                    Debug.Assert(modelCulture != null, "bCulture != null");

                    db.ProductCultures.RemoveRange(model.ProductCultures);
                    db.Products.Remove(model);
                    var result = await db.SaveChangesAsync();
                    if (result == 0)
                    {
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified, string.Format(NOWCulture.GetString("Can not remove {0}{1}."), NOWCulture.GetString("Product"), " '" + modelCulture.Name + "'"));
                    }

                    return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK);
                }
            }
            catch (Exception ex)
            {
                LogHelper.Write(ex);
                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.InternalServerError, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
            }
        }

        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> ProductSpecifications(int id)
        {
            var product = await DatabaseConnector.Products.SingleOrDefaultAsync(i => i.Id == id);
            if (product == null)
            {
                return HttpNotFound();
            }

            ViewBag.SpecificationGroups = product.ProductCategory.SpecificationGroups.OrderBy(i => i.DisplayOrder);
            if (!string.IsNullOrEmpty(product.Specifications))
            {
                ViewBag.ProductSpecifications = JsonConvert.DeserializeObject<ProductViewModel.ProductSpecification[]>(product.Specifications);
            }
            return View(product);
        }

        [HttpPost]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> ProductSpecifications(int id, string jsonData)
        {
            try
            {
                using (var db = DatabaseConnector)
                {
                    var product = await db.Products.SingleOrDefaultAsync(i => i.Id == id);
                    if (product != null)
                    {
                        product.Specifications = jsonData;
                    }

                    if (db.ChangeTracker.HasChanges())
                    {
                        var result = await db.SaveChangesAsync();
                        if (result > 0)
                        {
                            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, Url.Action("Products"));
                        }
                        return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified, string.Format(NOWCulture.GetString("Create/Update {0} failed."), NOWCulture.GetString("specifications")));
                    }
                    return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, Url.Action("Products"));
                }
            }
            catch (Exception ex)
            {
                LogHelper.Write(ex);
                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.InternalServerError, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
            }
        }

        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> ProductGallery(string selectedCulture, int id)
        {
            using (var db = DatabaseConnector)
            {
                var product = await db.Products.SingleOrDefaultAsync(i => i.Id == id);

                if (product == null)
                {
                    return HttpNotFound();
                }

                if (string.IsNullOrWhiteSpace(selectedCulture))
                {
                    selectedCulture = NOWCulture.CurrentCulture;
                }

                var productCulture = product.ProductCultures.SingleOrDefault(i => i.Culture == selectedCulture);

                if (productCulture == null)
                {
                    return HttpNotFound();
                }

                var imageModels = string.IsNullOrEmpty(productCulture.Gallery) ? new List<ProductImage>() : JsonConvert.DeserializeObject<List<ProductImage>>(productCulture.Gallery);
                var images = imageModels.Select(imgModel => new ProductImageViewModel(id, selectedCulture, imgModel)).ToList();
                ViewBag.Images = images;

                return View(productCulture);
            }
        }

        [HttpGet]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> EditProductImage(int productId, string selectedCulture, string imageSrc)
        {
            using (var db = DatabaseConnector)
            {
                var product = await db.Products.SingleOrDefaultAsync(i => i.Id == productId);

                if (product == null)
                {
                    return HttpNotFound();
                }

                if (string.IsNullOrWhiteSpace(selectedCulture))
                {
                    selectedCulture = NOWCulture.CurrentCulture;
                }

                var productCulture = product.ProductCultures.SingleOrDefault(i => i.Culture == selectedCulture);

                if (productCulture == null)
                {
                    return HttpNotFound();
                }

                ProductImageViewModel model = null;
                if (string.IsNullOrEmpty(imageSrc))
                {
                    model = new ProductImageViewModel { ProductId = productId, Culture = selectedCulture };
                }
                else
                {
                    if (!string.IsNullOrEmpty(productCulture.Gallery))
                    {
                        List<ProductImage> images = JsonConvert.DeserializeObject<List<ProductImage>>(productCulture.Gallery);
                        var image = images.SingleOrDefault(i => i.Src == imageSrc);
                        if (image != null)
                        {
                            model = new ProductImageViewModel(productId, selectedCulture, image);
                        }
                    }
                }

                if (model == null)
                {
                    model = new ProductImageViewModel { ProductId = productId, Culture = selectedCulture };
                }

                ViewBag.IsEdit = !string.IsNullOrEmpty(model.Src);
                return PartialView(model);
            }
        }

        [HttpPost]
        [Compress]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> EditProductImage(ProductImageViewModel editModel)
        {
            try
            {
                if (string.IsNullOrEmpty(editModel.Src))
                {
                    ModelState.AddModelError("Image", NOWCulture.GetString("Please choose either a GIF, JPG or PNG image."));
                }

                if (ModelState.IsValid)
                {
                    using (var db = DatabaseConnector)
                    {
                        var product = await db.Products.SingleOrDefaultAsync(i => i.Id == editModel.ProductId);
                        if (product == null)
                        {
                            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.BadRequest, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("Product")));
                        }

                        var productCulture = product.ProductCultures.SingleOrDefault(i => i.Culture == editModel.Culture);
                        if (productCulture == null)
                        {
                            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.BadRequest, string.Format(NOWCulture.GetString("{0} is not found."), NOWCulture.GetString("The product informations of selected language")));
                        }

                        if (!string.IsNullOrEmpty(editModel.Src))
                        {
                            List<ProductImage> images = string.IsNullOrEmpty(productCulture.Gallery) ? new List<ProductImage>() : JsonConvert.DeserializeObject<List<ProductImage>>(productCulture.Gallery);

                            var image = images.SingleOrDefault(i => i.Src == editModel.Src);
                            if (image != null)
                            {
                                image.Src = editModel.Src;
                                image.Caption = editModel.Caption;
                                image.AltText = editModel.AltText;
                                image.IsAvatar = editModel.IsAvatar;
                            }
                            else
                            {
                                image = new ProductImage
                                {
                                    Src = editModel.Src,
                                    Caption = editModel.Caption,
                                    AltText = editModel.AltText,
                                    IsAvatar = editModel.IsAvatar
                                };
                                images.Add(image);
                            }

                            productCulture.Gallery = JsonConvert.SerializeObject(images);
                            var result = await db.SaveChangesAsync();
                            if (result > 0)
                            {
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK, editModel.Src, RenderPartialViewToString("_ProductImageView", editModel));
                            }
                            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ModelState.AddModelError(string.Empty, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
                LogHelper.Write(ex);
            }

            ViewBag.IsEdit = !string.IsNullOrEmpty(editModel.Src);
            return PartialView(editModel);
        }

        [HttpPost]
        [Compress]
        [Authorize(Roles = "Administrators, Editors, Product Editors")]
        public async Task<ActionResult> RemoveProductImage(int productId, string selectedCulture, string imageSrc)
        {
            try
            {
                using (var db = DatabaseConnector)
                {
                    var product = await db.Products.SingleOrDefaultAsync(i => i.Id == productId);

                    if (product == null)
                    {
                        return HttpNotFound();
                    }

                    if (string.IsNullOrWhiteSpace(selectedCulture))
                    {
                        selectedCulture = NOWCulture.CurrentCulture;
                    }

                    var productCulture = product.ProductCultures.SingleOrDefault(i => i.Culture == selectedCulture);

                    if (productCulture == null)
                    {
                        return HttpNotFound();
                    }

                    if (!string.IsNullOrEmpty(productCulture.Gallery))
                    {
                        List<ProductImage> images = JsonConvert.DeserializeObject<List<ProductImage>>(productCulture.Gallery);
                        var image = images.SingleOrDefault(i => i.Src == imageSrc);
                        if (image != null)
                        {
                            images.Remove(image);

                            productCulture.Gallery = JsonConvert.SerializeObject(images);
                            var result = await db.SaveChangesAsync();
                            if (result > 0)
                            {
                                Utilities.RemoveImage(imageSrc);
                                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.OK);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogHelper.Write(ex);
                return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.InternalServerError, NOWCulture.GetString("There are system errors, please contact with developers to handle."));
            }

            return new ViewHelper.HttpStatusCodeJsonResult(HttpStatusCode.NotModified);
        }
        #endregion

        #region URLs
        public ActionResult UrlList(string keywords)
        {
            ViewBag.SearchingUrl = keywords;
            return View(SEOHelper.URLDictionary);
        }
        #endregion

        #region Settings

        [Compress]
        [HttpGet]
        [Authorize(Roles = "Administrators")]
        public  ActionResult EditSettings()
        {
            return View(new SettingsViewModel());
        }

        [Compress]
        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize(Roles = "Administrators")]
        public ActionResult EditSettings(SettingsViewModel model)
        {
            if (ModelState.IsValid)
            {
                var dic = new Dictionary<string, string>
                {
                    {"CompanyName", model.CompanyName},
                    {"Copyright", model.Copyright},
                    {"Keywords", model.Keywords},
                    {"Description", model.Description}
                };
                Settings.UpdateSettings(dic);
            }
            return View(new SettingsViewModel());
        }

        #endregion

        #region Banners

        #endregion
    }
}